package com.leyou.item.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.dto.SpuDetailDTO;
import com.leyou.item.entity.*;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.service.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements SpuService {

    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SpuDetailService detailService;
    @Autowired
    private SkuService skuService;

    @Autowired
    private SpecParamService paramService;

    /* @Override
     public PageDTO<SpuDTO> querySpuByPage(
             Integer page, Integer rows, Boolean saleable, Long categoryId, Long brandId, Long id) {
         // 1.健壮性
         int current = Math.max(page, 1);
         int size = Math.max(rows, 5);

         // 2.准备查询条件， SELECT * FROM tb_spu WHERE saleable = ? AND category_id = ? AND brand_id = ? AND id = ?
         Page<Spu> result = query()
                 .eq(saleable != null, "saleable", saleable)
                 .eq(categoryId != null, "cid3", categoryId)
                 .eq(brandId != null, "brand_id", brandId)
                 .eq(id != null, "id", id)
                 // 3.准备分页条件 LIMIT ?, ?
                 .page(new Page<>(current, size));

         // 4.解析查询结果
         long total = result.getTotal();
         long pages = result.getPages();
         List<Spu> list = result.getRecords();

         // 5.转换DTO
         List<SpuDTO> dtoList = SpuDTO.convertEntityList(list);
         for (SpuDTO spuDTO : dtoList) {
             // 查询spu的分类和品牌的名称
             handleCategoryAndBrandName(spuDTO);
         }

         // 6.封装分页结果并返回
         return new PageDTO<>(total, pages, dtoList);
     }*/
    @Override
    public PageDTO<SpuDTO> SpuPage(Integer page, Integer rows, Long id, Long cid, Long bid, Boolean saleable) {

        // 1.健壮性
        int current = Math.max(page, 1);
        int size = Math.max(rows, 5);

        IPage<Spu> iPage = new Page<>(page, rows);

        page(iPage,new QueryWrapper<Spu>()
                .eq(null!=id,"id",id)
                .eq(null!=cid,"cid3",cid)
                .eq(null!=bid,"brand_id",bid)
                .eq(null!=saleable,"saleable",saleable)
        );

        //获取分页后的数据信息
        List<Spu> spus = iPage.getRecords();
        //将信息封装为DTO
        List<SpuDTO> spuDTOS = SpuDTO.convertEntityList(spus);
        //循环 DTO 根据 DTO中的 分类id 和 品牌id 查询到对应的名字
        spuDTOS.forEach(spuDTO -> {

            //TODO 健壮性处理，查询必须保证查询参数有效

            //TODO 分类以及品牌的查询要考虑缓存问题，
            spuDTO.setBrandName(brandService.getById(spuDTO.getBrandId()).getName());

            //获取category的 name
            String c_name = this.categoryService.listByIds(spuDTO.getCategoryIds())
                    .stream()
                    .map(Category::getName)
                    .collect(Collectors.joining("/"));
            spuDTO.setCategoryName(c_name);
        });

        //
        return new PageDTO<>(iPage.getTotal(),iPage.getPages(),spuDTOS);
    }

    @Override
    @Transactional
    public void addGoods(SpuDTO spuDTO) {

        //保存Spu信息
        Spu spu = spuDTO.toEntity(Spu.class);
        save(spu);
        //保存spuDetail
        SpuDetail spuDetail = spuDTO.getSpuDetail().toEntity(SpuDetail.class);
        spuDetail.setSpuId(spu.getId());
        detailService.save(spuDetail);

        //保存sku
        List<Sku> skus = spuDTO.getSkus().stream()
                .map(skuDTO -> {
                    Sku sku = skuDTO.toEntity(Sku.class);
                    sku.setId(spu.getId());
                    return sku;
                }).collect(Collectors.toList());

        //批量保存
        this.skuService.saveBatch(skus);
    }


    @Override
    public SpuDTO queryGoodsById(Long id) {


        //TODO 根据id查询本身不会有问题，但是有可能查不到，get(0)可能有bug，建议进行健壮性处理
        SpuDTO spuDTO = this.SpuPage(1, 1, id, null, null, null).getItems().get(0);

        //根据spuId查询对应的spuDetail
        spuDTO.setSpuDetail(new SpuDetailDTO(this.detailService.getById(id)));
        //根据spuId查询对应的sku集合，List<Entity>==>List<DTO>
        spuDTO.setSkus(SkuDTO.convertEntityList(this.skuService.query().eq("spu_id",id).list()));

        return spuDTO;
    }

    @Override
    public SpuDTO querySpuById(Long id) {
        return new SpuDTO(this.getById(id));
    }

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    @Transactional
    public void updateSaleable(Long id, Boolean saleable) {
        Spu spu = new Spu();
        spu.setId(id);
        spu.setSaleable(saleable);
        //update tb_spu set saleable = #{saleable} where id = #{id}
        updateById(spu);
        //修改sku表数据
        Sku sku = new Sku();
        sku.setSpuId(id);
        sku.setSaleable(saleable);
        //update tb_sku set saleable = #{saleable} where spu_id = #{id} and saleable = !#{saleable}
        skuService.update(sku, new QueryWrapper<Sku>()
                .eq("spu_id", id)
                .eq("saleable",!saleable));

        //saleable 为true表示上架,为false为下架
        String routingKey = saleable ? "up" : "down";

        this.amqpTemplate.convertAndSend("jhj",routingKey,spu.getId());
    }

    @Override
    public List<SpecParamDTO> querySpecParamBySpuId(Long id, Boolean searching) {
        Long categoryId = this.getById(id).getCid3();

        //select * from tb_spec_param where category_id = #{categoryId} [and searching = #{searching}]
        List<SpecParamDTO> specParamDTOS = SpecParamDTO.convertEntityList(this.paramService.query()
                .eq("category_id", categoryId)
                .eq(null != searching, "searching", searching)
                .list());



        //key ==>paramId,value,就是需要的value
        Map<Long,Object> paramValueMap =
                JsonUtils.nativeRead(
                        this.detailService.getById(id).getSpecification(),
                        new TypeReference<Map<Long, Object>>() {
                        });


        specParamDTOS.forEach(specParamDTO -> {
            Long paramId = specParamDTO.getId();
            specParamDTO.setValue(paramValueMap.get(paramId));
        });

        return specParamDTOS;    }

    @Override
    @Transactional
    public void updateGoods(SpuDTO spuDTO) {

        //修改，修改谁？？？？

        //修改spu
        if (null!=spuDTO.getId()){
            updateById(spuDTO.toEntity(Spu.class));
        }

        //修改spuDetail
        if (null!=spuDTO.getSpuDetail()){
            this.detailService.updateById(spuDTO.getSpuDetail().toEntity(SpuDetail.class));
        }

        //修改sku
        if (null!=spuDTO.getSkus() && 0 != spuDTO.getSkus().size()){

            //把提交的skuDTO集合转换为sku的集合
            List<Sku> skus = spuDTO.getSkus().stream().map(skuDTO -> {
                Sku sku = skuDTO.toEntity(Sku.class);
                return sku;
            }).collect(Collectors.toList());


            //map就是分组的结果，key，true或者false，value就是key为true或者false的对应的集合
            Map<Boolean, List<Sku>> skuMap = skus
                    .stream()
                    .collect(Collectors.groupingBy(sku -> sku.getSaleable() == null));

            //获取到要修改的或者新增的
            List<Sku> skusToBeUpdateOrSave = skuMap.get(true);
            this.skuService.saveOrUpdateBatch(skusToBeUpdateOrSave);

            List<Sku> skusToBeDelete = skuMap.get(false);

            List<Long> ids = skusToBeDelete.stream().map(Sku::getId).collect(Collectors.toList());

            //根据id集合删除
            this.skuService.removeByIds(ids);
        }
    }



}
